﻿using System;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media.Animation;
using BusinessIntelligenceToolkit.ViewModels;

namespace BusinessIntelligenceToolkit.Controls
{
    [TemplatePart(Name = ItemsHostPanelName, Type = typeof(BitStackPanel))]
    [TemplatePart(Name = VerticalScrollBarName, Type = typeof(ScrollBar))]
    public class BitScrollViewer : Control
    {
        public static readonly DependencyProperty OrientationProperty =
            DependencyProperty.Register("Orientation", typeof(Orientation), typeof(BitScrollViewer), new PropertyMetadata(Orientation.Vertical));

        public static readonly DependencyProperty MaximumProperty =
            DependencyProperty.Register("Maximum", typeof(double), typeof(BitScrollViewer), new PropertyMetadata(default(double)));

        public static readonly DependencyProperty ItemsControlProperty =
            DependencyProperty.Register("ItemsControl", typeof(BitItemsControl), typeof(BitScrollViewer), new PropertyMetadata(OnItemsControlChanged));

        public static readonly DependencyProperty CurrentPositionProperty =
            DependencyProperty.Register("CurrentPosition", typeof(double), typeof(BitScrollViewer), new PropertyMetadata(OnCurrentPositionChanged));

        public static readonly DependencyProperty CollectionViewProperty =
            DependencyProperty.Register("CollectionView", typeof(ICollectionView), typeof(BitScrollViewer), new PropertyMetadata(default(ICollectionView), OnCollectionViewChanged));

        public static readonly DependencyProperty BringCurrentIntoViewProperty =
           DependencyProperty.RegisterAttached(
           "BringCurrentIntoView",
           typeof(bool),
           typeof(BitScrollViewer),
           new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.Inherits));

        public static readonly DependencyProperty AnimationProperty =
            DependencyProperty.Register("Animation", typeof(DoubleAnimation), typeof(BitScrollViewer), new PropertyMetadata(default(DoubleAnimation), OnAnimationChanged));

        private const string ItemsHostPanelName = "PART_ItemsHost";
        private const string VerticalScrollBarName = "PART_VerticalScrollBar";

        private Storyboard _sb;

        private BitStackPanel _itemsPanel = null;
        private bool _animationLocked = false;
        
        static BitScrollViewer()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(BitScrollViewer), new FrameworkPropertyMetadata(typeof(BitScrollViewer)));
        }

        public BitScrollViewer()
        {
            VerticalContentAlignment = VerticalAlignment.Stretch;
            Loaded += (s, e) =>
                          {
                              ApplyTemplate();
                              SetDefaultSmallChange();
                          };

            MouseWheel += (s, e) =>
                              {
                                  if (e.Delta < 0)
                                  {
                                      VerticalScrollBar.Value = VerticalScrollBar.Value +
                                                                 VerticalScrollBar.SmallChange;
                                  }
                                  else
                                  {
                                      VerticalScrollBar.Value = VerticalScrollBar.Value -
                                                                 VerticalScrollBar.SmallChange;
                                  }
                              };
            
            SizeChanged += (s, e) =>
                               {
                                   bool fromBottom = true;
                                   double offset = 0;
                                   if (CollectionView != null && _itemsPanel != null && e.HeightChanged)
                                   {
                                       if (!_itemsPanel.IsScrolling)
                                       {
                                           fromBottom =
                    _itemsPanel.Children.Cast<UIElement>().Any(
                        x =>
                        {
                            var elementPositionOnPanel = x.TranslatePoint(new Point(0, x.DesiredSize.Height), _itemsPanel).Y;

                            var containsLast = ItemsControl
                                .ContainerGenerator
                                .ContainerContainsEqualData(x, CollectionView.GetItemAt(CollectionView.Count - 1));

                            if (containsLast)
                            {
                                offset = Math.Floor((_itemsPanel.DesiredSize.Height - Math.Floor(elementPositionOnPanel)) / x.DesiredSize.Height);
                            }

                            return containsLast && _itemsPanel.DesiredSize.Height - Math.Floor(elementPositionOnPanel) >= x.DesiredSize.Height;
                        });

                                           var oldValue = VerticalScrollBar.Value;
                                           var newMaximum = CollectionView.Count - _itemsPanel.ContainersInViewport;

                                           ChangeMaximumWithoutAnimation(newMaximum);
                                           if (oldValue > newMaximum)
                                           {
                                               ChangeValueWithoutAnimation(newMaximum);
                                           }

                                           if (FirstInViewportandFit())
                                           {
                                               return;
                                           }

                                           if (fromBottom)
                                           {
                                               IsHeightChanged = true;

                                               _itemsPanel.InvalidateMeasure();
                                               _itemsPanel.UpdateLayout();
                                               _itemsPanel.InvalidateArrange();
                                               _itemsPanel.UpdateLayout();

                                               IsHeightChanged = false;

                                               ChangeMaximumWithoutAnimation(VerticalScrollBar.Maximum - offset);
                                               if (oldValue > newMaximum)
                                               {
                                                   ChangeValueWithoutAnimation(VerticalScrollBar.Maximum - offset);
                                               }
                                           }
                                       }
                                   }
                               };
        }

        public DoubleAnimation Animation
        {
            get { return (DoubleAnimation)GetValue(AnimationProperty); }
            set { SetValue(AnimationProperty, value); }
        }

        public Orientation Orientation
        {
            get { return (Orientation)GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }

        public double Maximum
        {
            get { return (double)GetValue(MaximumProperty); }
            set { SetValue(MaximumProperty, value); }
        }

        public BitItemsControl ItemsControl
        {
            get { return (BitItemsControl)GetValue(ItemsControlProperty); }
            set { SetValue(ItemsControlProperty, value); }
        }

        public double CurrentPosition
        {
            get { return (double)GetValue(CurrentPositionProperty); }
            set { SetValue(CurrentPositionProperty, value); }
        }

        public ListCollectionView CollectionView
        {
            get { return (ListCollectionView)GetValue(CollectionViewProperty); }
            set { SetValue(CollectionViewProperty, value); }
        }

        public ScrollBar VerticalScrollBar { get; private set; }

        public bool IsScrolling { get; private set; }

        public bool IsHeightChanged { get; private set; }

        internal UIElementCollection Containers
        {
            get
            {
                return _itemsPanel.Children;
            }
        }

        public static void SetBringCurrentIntoView(UIElement element, bool value)
        {
            element.SetValue(BringCurrentIntoViewProperty, value);
        }

        public static bool GetBringCurrentIntoView(UIElement element)
        {
            return (bool)element.GetValue(BringCurrentIntoViewProperty);
        }

        public void BringCurrentIntoView()
        {
            if (CollectionView != null)
            {
                ScrollTo(CollectionView.CurrentPosition);
            }
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _itemsPanel = (BitStackPanel)GetTemplateChild(ItemsHostPanelName);
            VerticalScrollBar = (ScrollBar)GetTemplateChild(VerticalScrollBarName);
            VerticalScrollBar.ValueChanged += OnVerticallScrollValueChanged;
        }

        private static void OnCollectionViewChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            BitScrollViewer instance = d as BitScrollViewer;
            ICollectionView oldCollectionView = e.OldValue as ICollectionView;
            ICollectionView newCollectionView = instance.CollectionView;
            instance.VerticalScrollBar.Maximum = 0;
            if (oldCollectionView != null)
            {
                oldCollectionView.CurrentChanged -= instance.OnCurrentChanged;
            }

            if (instance.CollectionView != null)
            {
                instance.CollectionView.CurrentChanged += instance.OnCurrentChanged;
            }

            if (oldCollectionView is NavigableListCollectionView)
            {
                (oldCollectionView as NavigableListCollectionView).BringCurrentIntoView -= instance.OnBringCurrentIntoView;
            }

            if (newCollectionView is NavigableListCollectionView)
            {
                (newCollectionView as NavigableListCollectionView).BringCurrentIntoView += instance.OnBringCurrentIntoView;
            }
        }

        private static void OnCurrentPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            BitScrollViewer instance = d as BitScrollViewer;
            instance._itemsPanel.Offset = instance.CurrentPosition;
        }

        private static void OnItemsControlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            BitScrollViewer instance = d as BitScrollViewer;
        }
       
        private static void OnAnimationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            BitScrollViewer instance = d as BitScrollViewer;
            if (instance.Animation == null)
            {
                return;
            }

            instance.Animation.Completed += (s, args) =>
            {
                var newMaximum = instance.CollectionView.Count -
                                 instance._itemsPanel.ContainersInViewport;
                if (instance._itemsPanel.Raised)
                {
                    //instance._itemsPanel.InvalidateMeasure();
                    //instance._itemsPanel.InvalidateArrange();
                    //instance._itemsPanel.UpdateLayout();
                    instance.VerticalScrollBar.Maximum = instance.CollectionView.Count - instance._itemsPanel.ContainersInViewport + 1;
                    //instance.VerticalScrollBar.Value = instance.VerticalScrollBar.Maximum ;
                }
                else if (newMaximum > instance.VerticalScrollBar.Maximum)
                {
                    instance.VerticalScrollBar.Maximum = newMaximum;
                }

                instance._itemsPanel.Raised = false;
                instance.IsScrolling = false;
            };
        }

        private bool LastInViewportAndNotFit()
        {
            var lastInViewportAndNotFit = _itemsPanel.Children.Cast<UIElement>().Any(
                        x =>
                        {
                            var elementPositionOnPanel = x.TranslatePoint(new Point(0, x.DesiredSize.Height), _itemsPanel).Y;
                            var containsLast = ItemsControl
                                .ContainerGenerator
                                .ContainerContainsEqualData(x, CollectionView.GetItemAt(CollectionView.Count - 1));

                            return containsLast && Math.Floor(elementPositionOnPanel) > _itemsPanel.DesiredSize.Height;
                        });

            return lastInViewportAndNotFit;
        }

        private bool FirstInViewportandFit()
        {
            var lastInViewportAndNotFit = _itemsPanel.Children.Cast<UIElement>().Any(
                        x =>
                        {
                            var elementPositionOnPanel = x.TranslatePoint(new Point(0, 0), _itemsPanel).Y;
                            var containsLast = ItemsControl
                                .ContainerGenerator
                                .ContainerContainsEqualData(x, CollectionView.GetItemAt(0));

                            return containsLast && Math.Floor(elementPositionOnPanel) >= 0;
                        });

            return lastInViewportAndNotFit;
        }

        private void ChangeValueWithoutAnimation(double value)
        {
            _animationLocked = true;
            VerticalScrollBar.Value = value;
            _animationLocked = false;
        }

        private void ChangeMaximumWithoutAnimation(double value)
        {
            _animationLocked = true;
            VerticalScrollBar.Maximum = value;
            _animationLocked = false;
        }

        private void SetDefaultSmallChange()
        {
            VerticalScrollBar.SmallChange = 1;
        }

        private void OnBringCurrentIntoView(object sender, EventArgs e)
        {
            BringCurrentIntoView();
        }

        private void OnCurrentChanged(object sender, EventArgs e)
        {
            var withBringIntoView = (bool)GetValue(BringCurrentIntoViewProperty);
        }

        private void ScrollTo(double value)
        {
            VerticalScrollBar.Value = value;
        }

        private void PrepareAnimation(double value)
        {
            _sb = new Storyboard();
            Animation.To = value;
            _sb.Children.Add(Animation);
            Storyboard.SetTarget(Animation, this);
            Storyboard.SetTargetProperty(Animation, new PropertyPath(CurrentPositionProperty));

            _sb.Begin(this);
        }

        private void OnVerticallScrollValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            MoveToPosition(Math.Floor(e.NewValue));
        }

        private void MoveToPosition(double position)
        {
            if (ItemsControl.Animation != null && !_animationLocked)
            {
                IsScrolling = true;
                PrepareAnimation(position);
            }
            else
            {
                BeginAnimation(CurrentPositionProperty, null);
                CurrentPosition = position;
            }

            SetDefaultSmallChange();
        }    
    }
}